home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 031a / adg_4_6.zip / PRINTSTP.C < prev    next >
C/C++ Source or Header  |  1991-02-21  |  22KB  |  592 lines

  1. /****************************************************************************
  2. Module name: PrintStp.C
  3. Programmer : Jeffrey M. Richter.
  4. *****************************************************************************/
  5.  
  6. #include "..\nowindws.h"
  7. #undef NOCTLMGR
  8. #undef NOGDI
  9. #undef NOKERNEL
  10. #undef NOLSTRING
  11. #undef NOMB
  12. #undef NOMEMMGR
  13. #undef NOMINMAX
  14. #undef NOMSG
  15. #undef NOSHOWWINDOW
  16. #undef NOUSER
  17. #undef NOWH
  18. #undef NOWINMESSAGES
  19. #undef NOWINOFFSETS
  20. #undef NOWINSTYLES
  21. #include <windows.h>
  22. #include <drivinit.h>
  23. #include <string.h>
  24.  
  25. #include "dialog.h"
  26. #include "printstp.h"
  27.  
  28.  
  29. extern HANDLE _hInstance;
  30. char _szPSSTRUCTProp[] = "PSSTRUCT";
  31. char _szPSModeProp[] = "PSMode";
  32.  
  33.  
  34. // User-defined messages for the PrinterSetupProc dialog box function.
  35. #define PSM_FILLPRINTERBOX       (WM_USER + 500)
  36. #define PSM_SETDEFAULTPRINTER    (WM_USER + 501)
  37.  
  38. // DRIVINIT.H doesn't define types for use with the old DeviceMode 
  39. // function so we have to.
  40. typedef void FAR PASCAL FNDEVICEMODE (HWND, HANDLE, LPSTR, LPSTR);
  41. typedef FNDEVICEMODE FAR *LPFNDEVICEMODE;
  42.  
  43.  
  44.  
  45. // Possible return values from the SetupNewPrinter function.
  46. #define SNP_SUCCESS        (0)
  47. #define SNP_NODRIVER       (1)
  48. #define SNP_INVALIDDRIVER  (2)
  49. #define SNP_NOMEMORY       (3)
  50. int NEAR PASCAL SetupNewPrinter (GLOBALHANDLE FAR *hPSOld, HWND hWnd, LPSTR szPrintInfo);
  51.  
  52. BOOL FAR PASCAL PrinterSetupProc (HWND hDlg, WORD wMsg, WORD wParam, LONG lParam);
  53.  
  54.  
  55. // Function called from application to present "Printer setup" box.
  56. // Returns handle to PSSTRUCT block for new printer or NULL if no change.
  57. GLOBALHANDLE FAR PASCAL PrinterSetup (HWND hWnd, GLOBALHANDLE hMem, WORD wMode) {
  58.    GLOBALHANDLE hNewMem; FARPROC fpProc;
  59.    fpProc = MakeProcInstance(PrinterSetupProc, _hInstance);
  60.    hNewMem = DialogBoxParam(_hInstance, "PrinterSetup", hWnd,
  61.       fpProc, MAKELONG(wMode, hMem));
  62.    FreeProcInstance(fpProc);
  63.    return(hNewMem);
  64. }
  65.  
  66.  
  67. // Dialog function for use by "Printer setup" box.
  68. // EndDialog returns handle of new PSSTRUCT block or NULL if no change.
  69. BOOL FAR PASCAL PrinterSetupProc (HWND hDlg, WORD wMsg, WORD wParam, LONG lParam) {
  70.    HWND hWndList;
  71.    RECT rc;
  72.    char szNullPort[MAXPORTLEN], szAllPrinters[5 * MAXPRINTINFOLEN];
  73.    char szPrintInfo[MAXPRINTINFOLEN], szBuf[2 * MAXPRINTINFOLEN];
  74.    char szPrintInfoDef[MAXPRINTINFOLEN], szDriverDef[MAXDRIVERLEN];
  75.    char szDeviceDef[MAXDEVICELEN], szPortDef[MAXPORTLEN];
  76.    char szDevice[MAXDEVICELEN], szDriver[MAXDRIVERLEN], szPort[MAXPORTLEN];
  77.    BOOL fProcessed = TRUE;
  78.    GLOBALHANDLE hMem;
  79.    LPPSSTRUCT lpPSOrig, lpPS;
  80.    WORD wMode;
  81.    LPSTR lpszCrntPort, lpszTemp, lpszPrinter, lpszError;
  82.    int nDefPrint, nNumPrinters, nSNPError, x, nDefID;
  83.  
  84.    switch (wMsg) {
  85.  
  86.    case WM_INITDIALOG:
  87.       // lParam: LOWORD = wMode, HIWORD = GLOBALHANDLE to PSSTRUCT
  88.  
  89.       if (HIWORD(lParam) != NULL) { // A printer has been setup.
  90.          // Create 2nd memory block same size as first.
  91.          hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT,
  92.             GlobalSize(HIWORD(lParam)));
  93.       } else {
  94.          // No printer has been setup.
  95.          hMem = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, sizeof(PSSTRUCT));
  96.       }
  97.  
  98.       if (hMem == NULL) {  // Not enough memory.
  99.          MessageBox(hDlg, "Insufficient memory.", NULL,
  100.             MB_OK | MB_ICONEXCLAMATION);
  101.          // Terminate dialog box notify caller of no change in printer.
  102.          EndDialog(hDlg, NULL);
  103.          break;
  104.       }
  105.  
  106.       if (HIWORD(lParam) != NULL) { // A Printer has been setup.
  107.          // Copy original printer information into new block so that 
  108.          // we do not alter the original block in any way.
  109.          lpPSOrig = (LPPSSTRUCT) GlobalLock(HIWORD(lParam));
  110.          lpPS = (LPPSSTRUCT) GlobalLock(hMem);
  111.          _fmemcpy(lpPS, lpPSOrig, (WORD) GlobalSize(hMem));
  112.          GlobalUnlock(hMem);
  113.          GlobalUnlock(HIWORD(lParam));
  114.       }
  115.  
  116.       // Set TABSTOP in listbox to right-edge of its window.
  117.       hWndList = GetDlgItem(hDlg, ID_PRINTERBOX);
  118.       GetClientRect(hWndList, &rc);
  119.       x = (rc.right * LOWORD(GetDialogBaseUnits())) / 4;
  120.       SendMessage(hWndList, LB_SETTABSTOPS, 1, (LONG) (int FAR *) &x);
  121.  
  122.       // Save the PSSTRUCT block as a window property.
  123.       SetProp(hDlg, _szPSSTRUCTProp, hMem);
  124.       // Save the dialog box's mode settings as a window property.
  125.       SetProp(hDlg, _szPSModeProp, LOWORD(lParam));
  126.  
  127.       // Fill the list box with the installed printer information.
  128.       SendMessage(hDlg, PSM_FILLPRINTERBOX, 0, 0l);
  129.       break;
  130.  
  131.    case WM_DESTROY:
  132.       // Remove the two properties from the window.
  133.       RemoveProp(hDlg, _szPSSTRUCTProp);
  134.       RemoveProp(hDlg, _szPSModeProp);
  135.       break;
  136.  
  137.    case WM_WININICHANGE:
  138.       // Check if user changed the list of printers from another 
  139.       // application (most likely - Control Panel).
  140.       if (lstrcmpi((LPSTR) lParam, "devices") != 0 && lParam != NULL)
  141.          break;
  142.  
  143.       // If printers have changes, refill the list box.
  144.       SendMessage(hDlg, PSM_FILLPRINTERBOX, 0, 0l);
  145.       break;
  146.  
  147.    case PSM_FILLPRINTERBOX:
  148.       // Retrieve mode value from window property.
  149.       wMode = GetProp(hDlg, _szPSModeProp);
  150.  
  151.       // Get list of all printers installed by user.
  152.       x = GetProfileString("devices", NULL, "", szAllPrinters,
  153.             sizeof(szAllPrinters));
  154.       if (x == sizeof(szAllPrinters) - 2) {  // Buffer too small.
  155.          MessageBox(hDlg,
  156.             "Too many printers installed.\tList truncated.", NULL,
  157.             MB_OK | MB_ICONEXCLAMATION);
  158.          lpszPrinter = szAllPrinters + sizeof(szAllPrinters) - 3;
  159.          while (lpszPrinter > szAllPrinters && *lpszPrinter != 0)
  160.             *lpszPrinter-- = 0;
  161.       }
  162.  
  163.       // Retrieve the name of the NullPort (usually "None").
  164.       GetProfileString("windows", "NullPort", "",
  165.          szNullPort, sizeof(szNullPort));
  166.  
  167.  
  168.       hWndList = GetDlgItem(hDlg, ID_PRINTERBOX);
  169.       // Tell the listbox NOT to update its display & empty it.
  170.       SendMessage(hWndList, WM_SETREDRAW, FALSE, 0);
  171.       SendMessage(hWndList, LB_RESETCONTENT, 0, 0L);
  172.  
  173.       // Fill the LISTBOX with printers from [devices] section of WIN.INI.
  174.       lpszPrinter = szAllPrinters;
  175.  
  176.       for (; *lpszPrinter != 0; lpszPrinter = _fstrchr(lpszPrinter, 0) + 1) {
  177.  
  178.          // Get the driver name and port for the printer.
  179.          GetProfileString("devices", lpszPrinter, "", szPrintInfo, sizeof(szPrintInfo));
  180.          // lpszPrinter = "IBM Graphics"
  181.          // szPrintInfo = "IBMGRX,LPT1:,COM2:"
  182.  
  183.          lpszCrntPort = _fstrchr(szPrintInfo, ','); *lpszCrntPort++ = 0;
  184.          // szPrintInfo  = "IBMGRX"
  185.          // lpszCrntPort = "LPT1:,COM2:"
  186.  
  187.          while (lpszCrntPort != NULL) {
  188.             // If another port exists, terminate the string at the comma.
  189.             lpszTemp = _fstrchr(lpszCrntPort, ',');
  190.             if (lpszTemp != NULL) *lpszTemp = 0;
  191.             // lpszCrntPort = "LPT1:"
  192.  
  193.             // If active only && printer NOT active, don't add to listbox.
  194.             if ((wMode & PSMODE_ACTIVEONLY) &&
  195.                 lstrcmpi(lpszCrntPort, szNullPort) == 0) {
  196.                ;  // Do nothing.
  197.             } else {
  198.                // Build string that has the following form:
  199.                //    "(device) ON (port)\t(device)=(driver),(port)"
  200.                // Example: "IBM Graphics on LPT1:\tIBM Graphics=IBMGRX,LPT1:"
  201.                wsprintf(szBuf, "%s on %s\t%s=%s,%s",
  202.                   (LPSTR) lpszPrinter, (LPSTR) lpszCrntPort,
  203.                   (LPSTR) lpszPrinter, (LPSTR) szPrintInfo,
  204.                   (LPSTR) lpszCrntPort);
  205.  
  206.                // Add string to listbox. Note, the listbox sorts the printers
  207.                // in alpha-order because LBS_SORT style used in dlg template.
  208.                SendMessage(hWndList, LB_ADDSTRING, 0, (LONG) (LPSTR) szBuf);
  209.             }
  210.  
  211.             // Find next port if it exists.
  212.             lpszCrntPort = lpszTemp;
  213.             if (lpszCrntPort != NULL) lpszCrntPort++;
  214.             // lpszCrntPort = "COM2:"
  215.          }
  216.       }
  217.  
  218.       nNumPrinters = (int) SendMessage(hWndList, LB_GETCOUNT, 0, 0);
  219.       if (nNumPrinters == 0) {
  220.          MessageBox(hDlg, "No printers installed.", NULL,
  221.             MB_OK | MB_ICONEXCLAMATION);
  222.  
  223.          // Sending the WM_COMMAND w/IDCANCEL to terminate the dialog box
  224.          // will free the memory allocated during the processing
  225.          // of WM_INITDIALOG message.
  226.          SendMessage(hDlg, WM_COMMAND, IDCANCEL,
  227.             MAKELONG(GetDlgItem(hDlg, IDCANCEL), BN_CLICKED));
  228.          break;
  229.       }
  230.  
  231.       // Listbox is filled, determine which printer to select initially.
  232.       SendMessage(hDlg, PSM_SETDEFAULTPRINTER, 0, 0);
  233.  
  234.       // Tell the listbox that it is now OK to update its display.
  235.       SendMessage(hWndList, WM_SETREDRAW, TRUE, 0);
  236.  
  237.       // Force the listbox to be completely redrawn.
  238.       InvalidateRect(hWndList, NULL, TRUE);
  239.       break;
  240.  
  241.    case PSM_SETDEFAULTPRINTER:
  242.       // Set up the default selection in the listbox.
  243.       hWndList = GetDlgItem(hDlg, ID_PRINTERBOX);
  244.  
  245.       // Retrieve handle to printer memory block from window property.
  246.       hMem = GetProp(hDlg, _szPSSTRUCTProp);
  247.  
  248.       // Get the information for the default printer selected by the user.
  249.       GetProfileString("windows", "device", "",
  250.          szPrintInfoDef, sizeof(szPrintInfoDef));
  251.  
  252.       if (*szPrintInfoDef != 0) {
  253.          ParsePrintInfo(szPrintInfoDef, FALSE, szDriverDef,
  254.             szDeviceDef, szPortDef);
  255.       } else { *szDeviceDef = *szDriverDef = *szPortDef = 0; }
  256.  
  257.       nDefPrint = -1;
  258.       nNumPrinters = (int) SendMessage(hWndList, LB_GETCOUNT, 0, 0);
  259.       // Cannot be zero because of check in PSM_FILLPRINTERBOX message.
  260.  
  261.       lpPS = (LPPSSTRUCT) GlobalLock(hMem);
  262.       for (x = 0; x < nNumPrinters; x++) {
  263.          // Get line 'x' from the listbox.
  264.          SendMessage(hWndList, LB_GETTEXT, x, (LONG) (LPSTR) szPrintInfo);
  265.  
  266.          // PrintInfo string is after the TAB ('\t') character.
  267.          ParsePrintInfo(_fstrchr(szPrintInfo, '\t') + 1, FALSE,
  268.             szDriver, szDevice, szPort);
  269.  
  270.          // Remember index in listbox where the "default printer" is located.
  271.          if (lstrcmpi(szDevice, szDeviceDef) == 0 &&
  272.              lstrcmpi(szPort, szPortDef) == 0) nDefPrint = x;
  273.  
  274.          // If device and port of listbox entry & printer in PSSTRUCT block
  275.          // match, set that printer as the default and stop the "for" loop.
  276.          if (lstrcmpi(szDevice, lpPS->DevMode.dmDeviceName) == 0 &&
  277.              lstrcmpi(szPort, lpPS->szPort) == 0) {
  278.             SendMessage(hWndList, LB_SETCURSEL, x, 0);
  279.             break;
  280.          }
  281.       }
  282.       GlobalUnlock(hMem);
  283.  
  284.       if (x == nNumPrinters) {
  285.          // Printer in memory block didn't match any printer in listbox.
  286.  
  287.          // Set selection to "default printer" if it was found.
  288.          SendMessage(hWndList, LB_SETCURSEL,
  289.             (nDefPrint == -1) ? 0 : nDefPrint, 0);
  290.       }
  291.       break;
  292.  
  293.  
  294.    case WM_COMMAND:
  295.       switch (wParam) {
  296.       case ID_PRINTERBOX:
  297.          if (HIWORD(lParam) != LBN_DBLCLK) break;
  298.  
  299.          // Get the ID of the default pushbutton.  Note, assume that the
  300.          // HIWORD from sending DM_GETDEFID is DC_HASDEFID.
  301.          nDefID = LOWORD(SendMessage(hDlg, DM_GETDEFID, 0, 0));
  302.  
  303.          // Simulate the user selecting the default pushbutton.
  304.          SendMessage(hDlg, WM_COMMAND, nDefID,
  305.             MAKELONG(GetDlgItem(hDlg, nDefID), BN_CLICKED));
  306.          break;
  307.  
  308.  
  309.       case ID_SETUP:
  310.       case IDOK:
  311.          if (HIWORD(lParam) != BN_CLICKED) break;
  312.  
  313.          hWndList = GetDlgItem(hDlg, ID_PRINTERBOX);
  314.  
  315.          // Get information about currently selected printer.
  316.          x = (int) SendMessage(hWndList, LB_GETCURSEL, 0, 0L);
  317.          SendMessage(hWndList, LB_GETTEXT, x, (LONG) (LPSTR) szPrintInfo);
  318.          // szPrintInfo = "Generic / Text Only on FILE:\tGeneric / Text Only=TTY,FILE:"
  319.  
  320.          // Get memory handle of current selected printer.
  321.          hMem = GetProp(hDlg, _szPSSTRUCTProp);
  322.  
  323.          // If OK button pressed, do setup without displaying the printer's 
  324.          // settings dialog box.
  325.          nSNPError = SetupNewPrinter(&hMem,
  326.             (wParam == IDOK) ? NULL : hDlg,
  327.             _fstrchr(szPrintInfo, '\t') + 1);
  328.  
  329.          // New printer setup block returned, save it in the window property.
  330.          if (nSNPError == SNP_SUCCESS)
  331.             SetProp(hDlg, _szPSSTRUCTProp, hMem);
  332.          else {
  333.             switch (nSNPError) {
  334.                case SNP_NODRIVER:
  335.                   lpszError = "Cannot find printer driver."; break;
  336.                case SNP_INVALIDDRIVER:
  337.                   lpszError = "Invalid printer driver."; break;
  338.                case SNP_NOMEMORY:
  339.                   lpszError = "Insufficient memory."; break;
  340.             }
  341.             MessageBox(hDlg, lpszError, NULL, MB_OK | MB_ICONEXCLAMATION);
  342.          }
  343.  
  344.          if (wParam == IDOK)  {
  345.             // Terminate the dialog box & return handle to new printer setup.
  346.             EndDialog(hDlg, hMem);
  347.          } else {
  348.             // Set the "Ok" button back to the default button.
  349.             SendMessage(hDlg, DM_SETDEFID, IDOK, 0);
  350.             SendDlgItemMessage(hDlg, ID_SETUP, BM_SETSTYLE, (int) BS_PUSHBUTTON, TRUE);
  351.             SendDlgItemMessage(hDlg, IDOK, BM_SETSTYLE, (int) BS_DEFPUSHBUTTON, TRUE);
  352.  
  353.             // Set the focus back to the listbox.
  354.             SetFocus(GetDlgItem(hDlg, ID_PRINTERBOX));
  355.          }
  356.          break;
  357.  
  358.       case IDCANCEL:
  359.          // Free memory block allocated by WM_INITDIALOG message.
  360.          GlobalFree(GetProp(hDlg, _szPSSTRUCTProp));
  361.  
  362.          // Return NULL to caller stating that no change to selected printer.
  363.          EndDialog(hDlg, NULL);
  364.          break;
  365.       }
  366.       break;
  367.  
  368.    default: fProcessed = FALSE; break;
  369.    }
  370.    return(fProcessed);
  371. }
  372.  
  373.  
  374. // Function accepts string in a PrintInfo format.  This format is:
  375. //       (device name)[= | ,](driver name),(port)
  376. // It then fills the buffers passed-in with parsed fields.
  377. // If fAppendDriverExt is TRUE, ".DRV" is appended to the driver's name.
  378. void FAR PASCAL ParsePrintInfo (LPSTR szPrintInfo,
  379.    BOOL fAppendDriverExt, LPSTR szDriver, LPSTR szDevice, LPSTR szPort) {
  380.    LPSTR p;
  381.    while (('=' != *szPrintInfo) && (',' != *szPrintInfo))
  382.       *szDevice++ = *szPrintInfo++;
  383.    *szDevice = 0;
  384.    szPrintInfo++;
  385.  
  386.    p = szDriver;
  387.    while (*szPrintInfo != ',') *szDriver++ = *szPrintInfo++;
  388.    *szDriver = 0;
  389.    if (_fstrchr(p, '.') == NULL && fAppendDriverExt)
  390.       lstrcpy(szDriver, ".DRV");
  391.    szPrintInfo++;
  392.  
  393.    while (*szPrintInfo != 0) *szPort++ = *szPrintInfo++;
  394.    *szPort = 0;
  395. }
  396.  
  397.  
  398.  
  399. // Function is called when user selects "Ok" or "Setup..." button from
  400. // "Printer setup" dialog box.  This function prepares a new PSSTRUCT 
  401. // memory block with information about the selected printer.  If hWnd 
  402. // is NOT NULL, the user is presented the printer's settings dialog box 
  403. // to change any settings.  The hWnd parameter is NULL when the user
  404. // selects the "Ok" button from the "Printer setup" dialog box.
  405. int NEAR PASCAL SetupNewPrinter (GLOBALHANDLE FAR *hPSOld, HWND hWnd, LPSTR szPrintInfo) {
  406.    char szDriver[MAXDRIVERLEN], szDevice[MAXDEVICELEN], szPort[MAXPORTLEN];
  407.    int nDevModeSize, nEnvSize, nDlgBoxResult;
  408.    LPPSSTRUCT lpPSOld, lpPSNew;
  409.    GLOBALHANDLE hPSNew;
  410.    HANDLE hDriver;
  411.    LPFNDEVMODE ExtDeviceMode;
  412.    LPFNDEVICEMODE DeviceMode;
  413.  
  414.    ParsePrintInfo(szPrintInfo, TRUE, szDriver, szDevice, szPort);
  415.    hDriver = LoadLibrary(szDriver);
  416.    if (hDriver < 32) return(SNP_NODRIVER);
  417.  
  418.    ExtDeviceMode = (LPFNDEVMODE) GetProcAddress(hDriver, PROC_EXTDEVICEMODE);
  419.    DeviceMode = (LPFNDEVICEMODE) GetProcAddress(hDriver, PROC_OLDDEVICEMODE);
  420.  
  421.    if (ExtDeviceMode == NULL && DeviceMode == NULL) {
  422.       FreeLibrary(hDriver);
  423.       return(SNP_INVALIDDRIVER);
  424.    }
  425.  
  426.    lpPSOld = (LPPSSTRUCT) GlobalLock(*hPSOld);
  427.  
  428.    if (ExtDeviceMode != NULL) {
  429.  
  430.       // Get size of complete DEVMODE structure.
  431.       nDevModeSize = ExtDeviceMode(hWnd, hDriver, NULL, szDevice,
  432.          szPort, NULL, NULL, 0);
  433.  
  434.       hPSNew = GlobalAlloc(GMEM_MOVEABLE, PSOVERHEAD + nDevModeSize);
  435.       if (hPSNew == NULL) {
  436.          FreeLibrary(hDriver);
  437.          return(SNP_NOMEMORY);
  438.       }
  439.  
  440.       lpPSNew = (LPPSSTRUCT) GlobalLock(hPSNew);
  441.       // Copy over our port and driver names to new memory block.
  442.       lstrcpy(lpPSNew->szPort, szPort);
  443.       lstrcpy(lpPSNew->szDriver, szDriver);
  444.       lpPSNew->PSStat = PSSTAT_EXTDEVMODE;
  445.       lpPSNew->nEnvSize = 0;
  446.  
  447.       if (lpPSOld->PSStat == PSSTAT_EXTDEVMODE) {
  448.  
  449.          if (lstrcmpi(lpPSOld->szDriver, szDriver)) {
  450.             // Setting-up a different printer.
  451.  
  452.             // Reset device-specific & driver-specific fields in DEVMODE.
  453.             lstrcpy(lpPSOld->DevMode.dmDeviceName, szDevice);
  454.             lpPSOld->DevMode.dmDriverVersion = 0;
  455.             lpPSOld->DevMode.dmDriverExtra = 0;
  456.          }
  457.  
  458.          // Return value is IDOK if successful.
  459.          nDlgBoxResult = ExtDeviceMode(hWnd, hDriver, &lpPSNew->DevMode,
  460.             szDevice, szPort, &lpPSOld->DevMode, NULL,
  461.             DM_MODIFY | ((hWnd == NULL) ? 0 : DM_PROMPT) | DM_COPY);
  462.  
  463.       } else {
  464.  
  465.          // Return value is IDOK if successful.
  466.          nDlgBoxResult = ExtDeviceMode(hWnd, hDriver, &lpPSNew->DevMode,
  467.             szDevice, szPort, NULL, NULL,
  468.             ((hWnd == NULL) ? 0 : DM_PROMPT) | DM_COPY);
  469.       }
  470.  
  471.    } else {
  472.  
  473.       // For all other cases, the DeviceMode function must be called.
  474.  
  475.       // If the old PSSTRUCT has a valid Printer Environment,
  476.       // set the printer environment.
  477.       if (lpPSOld->nEnvSize > 0) {
  478.          SetEnvironment(szPort, (LPSTR) &lpPSOld->DevMode, lpPSOld->nEnvSize);
  479.       }
  480.  
  481.       if (hWnd != NULL)
  482.          DeviceMode(hWnd, hDriver, szDevice, szPort);
  483.  
  484.       // A valid return value is NOT guaranteed from the DeviceMode function
  485.       // so we will assume that it is IDOK.
  486.       nDlgBoxResult = IDOK;
  487.  
  488.       // nEnvSize is the size of the Printer Environment.
  489.       nEnvSize = GetEnvironment(szPort, NULL, 0);
  490.  
  491.       // Allocate a PSSTRUCT large enough to contain the Printer Environment.
  492.       hPSNew = GlobalAlloc(GMEM_MOVEABLE,
  493.          (nEnvSize == 0) ? sizeof(PSSTRUCT) : nEnvSize + PSOVERHEAD);
  494.       if (hPSNew == NULL) {
  495.          FreeLibrary(hDriver);
  496.          return(SNP_NOMEMORY);
  497.       }
  498.  
  499.       lpPSNew = (LPPSSTRUCT) GlobalLock(hPSNew);
  500.       // Copy over our port and driver names to new memory block.
  501.       lstrcpy(lpPSNew->szPort, szPort);
  502.       lstrcpy(lpPSNew->szDriver, szDriver);
  503.       lpPSNew->PSStat = PSSTAT_DEVMODEONLY;
  504.       lpPSNew->nEnvSize = nEnvSize;
  505.  
  506.       if (nEnvSize > 0) {
  507.          // If a Printer Environment exists, save it.
  508.          GetEnvironment(szPort, (LPSTR) &lpPSNew->DevMode, nEnvSize);
  509.       } else {
  510.          lstrcpy(lpPSNew->DevMode.dmDeviceName, szDevice);
  511.       }
  512.    }
  513.  
  514.    GlobalUnlock(*hPSOld);
  515.    FreeLibrary(hDriver);
  516.    GlobalUnlock(hPSNew);
  517.  
  518.    // nDlgBoxResult == IDOK if ExtDeviceMode or DeviceMode are successful.
  519.    if (nDlgBoxResult == IDOK) {
  520.       GlobalFree(*hPSOld);
  521.       *hPSOld = hPSNew;
  522.    } else
  523.       GlobalFree(hPSNew);
  524.  
  525.    return(SNP_SUCCESS);
  526. }
  527.  
  528.  
  529. // This function accepts a PSSTRUCT and creates a device context for the 
  530. // printer described in the structure.  If fUseDefaultSettings is TRUE,
  531. // the default settings are used for the DC and not any settings that may
  532. // exist in the PSSTRUCT set by a call to PrinterSetup.
  533. HDC FAR PASCAL CreatePrinterDC (GLOBALHANDLE hMem) {
  534.    LPPSSTRUCT lpPS;
  535.    HDC hDC;
  536.    LPSTR p;
  537.    LPDEVMODE lpDM = NULL;
  538.  
  539.    lpPS = (LPPSSTRUCT) GlobalLock(hMem);
  540.  
  541.    if (lpPS->PSStat == PSSTAT_EXTDEVMODE) {
  542.       // Set lpDM to point to valid DEVMODE structure.
  543.       lpDM = &lpPS->DevMode;
  544.    } else {
  545.       // lpPS->PSStat must be PSSTAT_DEVMODEONLY or PSSTAT_UNKNOWN.
  546.       if (lpPS->nEnvSize > 0) {
  547.          // If a printer environment exists for this printer use it.
  548.          lpDM = &lpPS->DevMode;
  549.       }
  550.    }
  551.  
  552.    // Remove extension from driver file name if it exists.
  553.    p = _fstrchr(lpPS->szDriver, '.');
  554.    if (p != NULL) *p = 0;
  555.  
  556.    // Create the device context.
  557.    hDC = CreateDC(lpPS->szDriver, lpPS->DevMode.dmDeviceName,
  558.       lpPS->szPort, (LPSTR) lpDM);
  559.  
  560.    // Restore the driver's extension if it was removed.
  561.    if (p != NULL) *p = '.';
  562.  
  563.    GlobalUnlock(hMem);
  564.    return(hDC);
  565. }
  566.  
  567.  
  568. // This function is called when an application initializes so that the 
  569. // information about the default printer is loaded into a PSSTRUCT.
  570. // Returns handle to PSSTRUCT block or NULL if insufficient memory.
  571. GLOBALHANDLE FAR PASCAL SetupDefPrinter (void) {
  572.    GLOBALHANDLE hMem = NULL; LPPSSTRUCT lpPS;
  573.    char szPrintInfo[MAXPRINTINFOLEN];
  574.    char szDriver[MAXDRIVERLEN], szDevice[MAXDEVICELEN], szPort[MAXPORTLEN];
  575.  
  576.    GetProfileString("windows", "device", "", szPrintInfo, sizeof(szPrintInfo));
  577.    if (*szPrintInfo == 0) return(hMem);   // No default printer selected.
  578.    // szPrintInfo="Generic / Text Only,TTY,FILE:"
  579.  
  580.    ParsePrintInfo(szPrintInfo, FALSE, szDriver, szDevice, szPort);
  581.    hMem = GlobalAlloc(GMEM_MOVEABLE, sizeof(PSSTRUCT));
  582.    if (hMem == NULL) return(hMem);
  583.    lpPS = (LPPSSTRUCT) GlobalLock(hMem);
  584.    lstrcpy(lpPS->szPort, szPort);
  585.    lstrcpy(lpPS->szDriver, szDriver);
  586.    lstrcpy(lpPS->DevMode.dmDeviceName, szDevice);
  587.    lpPS->PSStat = PSSTAT_UNKNOWN;
  588.    lpPS->nEnvSize = 0;
  589.    GlobalUnlock(hMem);
  590.    return(hMem);
  591. }
  592.